package cn.demoncat.util.io;

import cn.demoncat.util.exception.IoRuntimeException;
import cn.demoncat.util.lang.ParamEmptyUtil;
import cn.demoncat.util.lang.StringUtil;
import cn.demoncat.util.lang.constant.CharsetConstant;
import cn.demoncat.util.lang.constant.StringConstant;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.ClassPathResource;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * IO处理
 * 
 * 注：流只能读写文件，不能读写目录；目录在流输出文件时自动创建
 * 
 * @author 延晓磊
 *
 * @since 2016年9月13日
 */
public final class IoUtil {

	//################################ IO流管理 ################################//
	
	/**
	 * 通过文件地址获取InputStream
	 * 
	 * @param path	文件绝对路径
	 * 
	 * @return InputStream
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static FileInputStream getInputStreamByFile(String path) {
		try {
			return new FileInputStream(path);
		} catch (FileNotFoundException e) {
			throw new IoRuntimeException("读取本地文件失败：path = " + path, e);
		}
	}
	
	/**
	 * 通过文件地址获取OutputStream（覆盖）
	 * 
	 * @param path	文件绝对路径
	 * 
	 * @return InputStream
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static FileOutputStream getOutputStreamByFile(String path) {
		try {
			return new FileOutputStream(path);
		} catch (FileNotFoundException e) {
			throw new IoRuntimeException("输出本地文件失败：path = " + path, e);
		}
	}
	
	/**
	 * 通过文件地址获取OutputStream（追加）
	 * 
	 * @param path	文件绝对路径
	 * 
	 * @return InputStream
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static FileOutputStream getOutputStreamByFileAppend(String path) {
		try {
			return new FileOutputStream(path,true);
		} catch (FileNotFoundException e) {
			throw new IoRuntimeException("输出本地文件失败：path = " + path, e);
		}
	}
	
	/**
	 * 通过ClassPath地址获取InputStream
	 * 
	 * @param path	文件相对路径，基于ClassPath，前缀非/
	 * 
	 * @return InputStream
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static InputStream getInputStreamByClassPath(String path) {
		// 转换路径名称
		ClassPathResource classPathResource = new ClassPathResource(path);
		try {
			// this.getClass().getResourceAsStream(path)
			return classPathResource.getInputStream();
		} catch (IOException e) {
			throw new IoRuntimeException("读取项目文件失败：path = " + path, e);
		}
	}
	
	/**
	 * 通过URL地址获取InputStream
	 * 
	 * @param url	文件链接，以http开头
	 * 
	 * @return InputStream
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static InputStream getInputStreamByUrl(String url) {
		try {
			// 打开远程连接，获取输入流
			return new URL(url).openStream();
		} catch (IOException e) {
			throw new IoRuntimeException("读取远程文件失败：url = " + url, e);
		}
	}
	
	/**
	 * 通过文件地址获取InputStream
	 * 
	 * @param path	文件绝对路径（以/或?:开头） || 文件ClassPath地址（前缀非/） || 文件URL地址（前缀http）
	 * 
	 * @return InputStream
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static InputStream getInputStreamByPath(String path) {
		if (path.startsWith("/")) {
			// 绝对路径：Linux
			return getInputStreamByFile(path);
		}else if (path.startsWith("http")) {
			// URL
			return getInputStreamByUrl(path);
		}else if (path.charAt(1) == ':') {
			// 绝对路径：Windows
			return getInputStreamByFile(path);
		}else {
			// 相对路径
			return getInputStreamByClassPath(path);
		}
	}
	
	/**
	 * 转换String为InputStream
	 * 
	 * @param str
	 * 
	 * @return InputStream
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static InputStream getInputStream(String str){
		return getInputStream(StringUtil.getBytes(str));
	}
	
	/**
	 * 转换byte[]为InputStream
	 * 
	 * @param bytes
	 * 
	 * @return InputStream
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static ByteArrayInputStream getInputStream(byte[] bytes){
		// 内存型InputStream，不需要close
		return new ByteArrayInputStream(bytes);
	}

	/**
	 * 获取byte[]输出流
	 *
	 * @return ByteArrayOutputStream
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static ByteArrayOutputStream getOutputStream(){
		return new ByteArrayOutputStream();
	}

	/**
	 * 将ByteArrayOutputStream转换为bytes
	 * 
	 * @param out
	 * @return  bytes
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年6月3日
	 */
	public static byte[] toBytes(ByteArrayOutputStream out){
		// 返回 byte[]，ByteArrayOutputStream不需要close(方法为空)
		return out.toByteArray();
	}
	
	
	/**
	 * 获取缓存流
	 * 
	 * @param reader
	 * @return 如果reader为空则返回null
	 */
	public static BufferedReader buffer(Reader reader){
		if (reader == null) {
			return null;
		}
		//获取缓冲流
		return IOUtils.buffer(reader);
	}
	
	/**
	 * 获取缓存流
	 * 
	 * @param writer 
	 * @return 如果writer为空则返回null
	 */
	public static BufferedWriter buffer(Writer writer){
		if (writer == null) {
			return null;
		}
		//获取缓冲流
		return IOUtils.buffer(writer);
	}
	
	/**
	 * 获取缓存流
	 * 
	 * @param ins 
	 * @return 如果ins为空则返回null
	 */
	public static BufferedInputStream buffer(InputStream ins){
		if (ins == null) {
			return null;
		}
		//获取缓冲流
		return IOUtils.buffer(ins);
	}
	
	/**
	 * 获取缓存流
	 * 
	 * @param out 
	 * @return 如果out为空则返回null
	 */
	public static BufferedOutputStream buffer(OutputStream out){
		if (out == null) {
			return null;
		}
		//获取缓冲流
		return IOUtils.buffer(out);
	}
	
	
	//################################ IO流属性 ################################//

	/**
	 * 比较两个输入流读取的内容是否相等
	 * 
	 * @param ins1
	 * @param ins2
	 * @return 如果为空则返回false
	 * @throws IoRuntimeException 如果获取流失败则抛出
	 */
	public static boolean equals(InputStream ins1, InputStream ins2){
		if (ParamEmptyUtil.isNull(ins1,ins2)) {
			return false;
		}
		try {
			return IOUtils.contentEquals(ins1, ins1);
		} catch (IOException e) {
			throw new IoRuntimeException("比较InputStream的内容失败", e);
		}
	}
	
	/**
	 * 比较两个读取流读取的内容是否相等
	 * 
	 * @param r1
	 * @param r2
	 * @return 如果为空则返回false
	 * @throws IoRuntimeException 如果获取流失败则抛出
	 */
	public static boolean equals(Reader r1, Reader r2){
		if (ParamEmptyUtil.isNull(r1,r2)) {
			return false;
		}
		try {
			return IOUtils.contentEquals(r1, r1);
		} catch (IOException e) {
			throw new IoRuntimeException("比较Reader的内容失败", e);
		}
	}

	/**
	 * 获取流的长度
	 * 
	 * @param in
	 * @return 可读取的长度
	 * 
	 * @author 延晓磊
	 *
	 * @since 2019年11月5日
	 */
	public static int size(InputStream in) {
		try {
			return in.available();
		} catch (IOException e) {
			throw new IoRuntimeException("读取文件失败", e);
		}
	}
	
	//################################ IO流读写 ################################//
	
	/**
	 * 读取InputStream输出到OutputStream，内部实现了缓存机制，适用于2G以下数据的复制
	 * 
	 * @param ins	输入流（已关闭）
	 * @param out	输出流（已关闭）
	 * 
	 * @return 读写的字节数，如果ins/out为空则返回0
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static long copy(InputStream ins, OutputStream out){
		if (ParamEmptyUtil.isNull(ins,out)) {
			return 0;
		}
		// 将InputStream的字节复制到OutputStream：内部实现缓存，所以无需使用Buffer；用于复制小的IO流(不超过2GB)
		try {
			return IOUtils.copy(ins, out);
		} catch (IOException e) {
			throw new IoRuntimeException("读取InputStream写出到OutputStream失败", e);
		}finally {
			CloseUtil.close(out);
			CloseUtil.close(ins);
		}
	}
	
	/**
	 * 读取InputStream输出到OutputStream，内部实现了缓存机制，适用于2G以上数据的复制
	 * 
	 * @param ins	输入流（已关闭）
	 * @param out	输出流（已关闭）
	 * @return 读写的字节数，如果ins/out为空则返回0
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static long copyBig(InputStream ins, OutputStream out){
		if (ParamEmptyUtil.isNull(ins,out)) {
			return 0;
		}
		// 将InputStream的字节复制到OutputStream：内部实现缓存，所以无需使用Buffer；用于复制大的IO流(超过2GB)
		try {
			return IOUtils.copyLarge(ins, out);
		} catch (IOException e) {
			throw new IoRuntimeException("读取InputStream写出到OutputStream失败", e);
		}finally {
			CloseUtil.close(out);
			CloseUtil.close(ins);
		}
	}
	
	/**
	 * 读取Reader输出到Writer，内部实现了缓存机制，适用于2G以下数据的复制
	 * 
	 * @param reader	输入流（已关闭）
	 * @param writer	输出流（已关闭）
	 * @return 读写的字符数，如果reader/writer为空则返回0
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static long copy(Reader reader, Writer writer){
		if (ParamEmptyUtil.isNull(reader,writer)) {
			return 0;
		}
		// 将Reader的字符复制到Writer：内部实现缓存，所以无需使用Buffer；用于复制小的IO流(不超过2GB)
		try {
			return IOUtils.copy(reader, writer);
		} catch (IOException e) {
			throw new IoRuntimeException("读取Reader写出到Writer失败", e);
		}finally {
			CloseUtil.close(reader);
			CloseUtil.close(writer);
		}
	}
	
	/**
	 * 读取Reader输出到Writer，内部实现了缓存机制，适用于2G以上数据的复制
	 * 
	 * @param reader	输入流（已关闭）
	 * @param writer	输出流（已关闭）
	 * @return 读写的字节符数，如果ins/writer为空则返回0
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static long copyBig(Reader reader, Writer writer){
		if (ParamEmptyUtil.isNull(reader,writer)) {
			return 0;
		}
		// 将Reader的字符复制到Writer：内部实现缓存，所以无需使用Buffer；用于复制大的IO流(超过2GB)
		try {
			return IOUtils.copyLarge(reader, writer);
		} catch (IOException e) {
			throw new IoRuntimeException("读取Reader写出到Writer失败", e);
		}finally {
			CloseUtil.close(reader);
			CloseUtil.close(writer);
		}
	}
	
	/**
	 * 读取InputStream的字节编码为字符输出到Writer，内部实现了缓存机制
	 * 
	 * @param ins		输入流（已关闭）
	 * @param writer	输出流（已关闭）
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void copy(InputStream ins, Writer writer){
		if (ParamEmptyUtil.isNull(ins,writer)) {
			return ;
		}
		// 将InputStream的字节编码为字符复制到Writer：内部实现缓存，所以无需使用Buffer
		try {
			IOUtils.copy(ins, writer, CharsetConstant.UTF8);
		} catch (IOException e) {
			throw new IoRuntimeException("读取InputStream写出到Writer失败", e);
		}finally {
			CloseUtil.close(ins);
			CloseUtil.close(writer);
		}
	}
	
	/**
	 * 读取Reader的字符编码为字节输出到OutputStream，内部实现了缓存机制
	 * 
	 * @param reader	输入流（已关闭）
	 * @param out		输出流（已关闭）
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void copy(Reader reader, OutputStream out){
		if (ParamEmptyUtil.isNull(reader,out)) {
			return ;
		}
		// 将InputStream的字节编码为字符复制到Writer：内部实现缓存，所以无需使用Buffer
		try {
			IOUtils.copy(reader, out, CharsetConstant.UTF8);
		} catch (IOException e) {
			throw new IoRuntimeException("读取Reader写出到OutputStream失败", e);
		}finally {
			CloseUtil.close(reader);
			CloseUtil.close(out);
		}
	}
 
	/**
	 * 将byte[]输出到OutputStream，适用于小型byte[]
	 * 
	 * @param bytes
	 * @param out	输出流（已关闭）
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void write(byte[] bytes, OutputStream out){
		if (ParamEmptyUtil.isNull(bytes,out)) {
			return ;
		}
		// 将byte[]写入OutputStream
		try {
			IOUtils.write(bytes, out);
		} catch (IOException e) {
			throw new IoRuntimeException("将byte[]写出到OutputStream失败", e);
		}finally {
			CloseUtil.close(out);
		}
	}
	
	/**
	 * 将byte[]编码为字符并输出到Wirter 
	 * 
	 * @param bytes
	 * @param writer	输出流（已关闭）
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void write(byte[] bytes, Writer writer){
		if (ParamEmptyUtil.isNull(bytes,writer)) {
			return ;
		}
		// 将byte[]写入Wirter
		try {
			IOUtils.write(bytes, writer,CharsetConstant.UTF8);
		} catch (IOException e) {
			throw new IoRuntimeException("将byte[]写出到Writer失败", e);
		}finally {
			CloseUtil.close(writer);
		}
	}
	
	/**
	 * 将字符串输出到Wirter 
	 * 
	 * @param str
	 * @param writer	输出流（已关闭）
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void write(CharSequence str, Writer writer){
		if (ParamEmptyUtil.isNull(str,writer)) {
			return ;
		}
		// 将CharSequence写入Wirter
		try {
			IOUtils.write(str, writer);
		} catch (IOException e) {
			throw new IoRuntimeException("将字符串写出到Writer失败", e);
		}finally {
			CloseUtil.close(writer);
		}
	} 
	
	/**
	 * 将字符串编码为byte[]输出到Wirter 
	 * 
	 * @param str
	 * @param out	输出流（已关闭）
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void write(CharSequence str, OutputStream out){
		write(str, out, CharsetConstant.UTF8);
	}
	
	/**
	 * 将字符串编码为byte[]输出到Wirter 
	 * 
	 * @param str
	 * @param out	输出流（已关闭）
	 * @param charset
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void write(CharSequence str, OutputStream out, String charset){
		if (ParamEmptyUtil.isNull(str,out)) {
			return ;
		}
		// 将CharSequence编码为byte[]写入OutputStream
		try {
			IOUtils.write(str, out,charset);
		} catch (IOException e) {
			throw new IoRuntimeException("将字符串写出到OutputStream失败", e);
		}finally {
			CloseUtil.close(out);
		}
	}
	
	/**
	 * 将字符串集合逐行输出到Wirter 
	 * 
	 * @param strs
	 * @param writer	输出流（已关闭）
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void write(List<String> strs, Writer writer){
		write(strs, writer, null);
	}
	
	/**
	 * 将字符串集合逐行输出到Wirter 
	 * 
	 * @param strs
	 * @param writer	输出流（已关闭）
	 * @param lineEnd 行尾符号，null 默认为"/r/n"
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void write(List<String> strs, Writer writer, String lineEnd){
		if (ParamEmptyUtil.isNull(strs,writer) || strs.isEmpty()) {
			return ;
		}
		if (null == lineEnd) {
			lineEnd = StringConstant.NEW_LINE;
		}
		// 将字符串集合写入Wirter，指定行末分隔符
		try {
			IOUtils.writeLines(strs,lineEnd,writer);
		} catch (IOException e) {
			throw new IoRuntimeException("将字符串集合写出到Writer失败", e);
		}finally {
			CloseUtil.close(writer);
		}
	}
	
	/**
	 * 将字符串集合逐行编码为byte[]输出到OutputStream 
	 * 
	 * @param strs
	 * @param out	输出流（已关闭）
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void write(List<String> strs, OutputStream out){
		write(strs, out, null);
	}
	
	/**
	 * 将字符串集合逐行编码为byte[]输出到OutputStream 
	 * 
	 * @param strs
	 * @param out	输出流（已关闭）
	 * @param lineEnd 行尾符号，null 默认为"/r/n"
	 * @throws IoRuntimeException  如果读写失败则抛出
	 */
	public static void write(List<String> strs, OutputStream out, String lineEnd){
		if (ParamEmptyUtil.isNull(strs,out) || strs.isEmpty()) {
			return ;
		}
		if (null == lineEnd) {
			lineEnd = StringConstant.NEW_LINE;
		}
		// 将字符串集合写入OutputStream，指定行末分隔符
		try {
			IOUtils.writeLines(strs,lineEnd,out,CharsetConstant.UTF8);
		} catch (IOException e) {
			throw new IoRuntimeException("将字符串集合写出到OutputStream失败", e);
		}finally {
			CloseUtil.close(out);
		}
	}

	/**
	 * 读取File为byte[]
	 *
	 * @param filePath		文件路径
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static byte[] readToBytes(String filePath){
		if (StringUtils.isBlank(filePath)) {
			return null;
		}
		return readToBytes(new File(filePath));
	}

	/**
	 * 读取File为byte[]
	 *
	 * @param file		文件
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static byte[] readToBytes(File file){
		if (file == null) {
			return null;
		}
		try {
			return FileUtils.readFileToByteArray(file);
		} catch (IOException e) {
			throw new IoRuntimeException("读取File为byte[]失败", e);
		}
	}

	/**
	 * 读取InputStream为byte[]
	 * 
	 * @param ins		输入流（已关闭）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static byte[] readToBytes(InputStream ins){
		if (ins == null) {
			return null;
		}
		try {
			// input > ByteArrayOutputStream > bytes
			return IOUtils.toByteArray(ins);
		} catch (IOException e) {
			throw new IoRuntimeException("读取InputStream为byte[]失败", e);
		} finally {
			CloseUtil.close(ins);
		}
	}
	
	/**
	 * 读取Reader为byte[]
	 * 
	 * @param reader
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static byte[] readToBytes(Reader reader){
		if (reader == null) {
			return null;
		}
		try {
			// reader > ByteArrayOutputStream > bytes
			return IOUtils.toByteArray(reader,CharsetConstant.UTF8);
		} catch (IOException e) {
			throw new IoRuntimeException("读取Reader为byte[]失败", e);
		} finally {
			CloseUtil.close(reader);
		}
	}
	
	/**
	 * 读取URL为byte[]
	 * 
	 * @param url
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static byte[] readToBytes(URL url){
		if (url == null) {
			return null;
		}
		try {
			return IOUtils.toByteArray(url);
		} catch (IOException e) {
			throw new IoRuntimeException("读取URL为byte[]失败", e);
		}
	}

	/**
	 * 读取InputStream为String
	 * 
	 * @param ins		输入流（已关闭）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static String readToStr(InputStream ins){
		return readToStr(ins, CharsetConstant.UTF8);
	}
	
	/**
	 * 读取InputStream为String
	 * 
	 * @param ins		输入流（已关闭）
	 * @param charset	字符集
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static String readToStr(InputStream ins, String charset){
		if (ins == null) {
			return null;
		}
		try {
			// input > StringWriter > String
			return IOUtils.toString(ins,charset);
		} catch (IOException e) {
			throw new IoRuntimeException("读取InputStream为String失败", e);
		} finally {
			CloseUtil.close(ins);
		}
	}
	
	/**
	 * 读取Reader为String
	 * 
	 * @param reader	输入流（已关闭）
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static String readToStr(Reader reader){
		if (reader == null) {
			return null;
		}
		try {
			// input > StringWriter > String
			return IOUtils.toString(reader);
		} catch (IOException e) {
			throw new IoRuntimeException("读取Reader为String失败", e);
		} finally {
			CloseUtil.close(reader);
		}
	}
	
	/**
	 * 读取URL为String
	 * 
	 * @param url	文件地址
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static String readToStr(URL url){
		if (url == null) {
			return null;
		}
		try {
			// input > StringWriter > String
			return IOUtils.toString(url, StandardCharsets.UTF_8);
		} catch (IOException e) {
			throw new IoRuntimeException("读取URL为String失败", e);
		} 
	}
	
	/**
	 * 读取InputStream为List[String]
	 * 
	 * @param ins	输入流（已关闭）	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static List<String> readToStrs(InputStream ins){
		return readToStrs(ins, CharsetConstant.UTF8);
	}

	/**
	 * 读取InputStream为List[String]
	 *
	 * @param ins	输入流（已关闭）
	 * @param charset
	 * @return
	 *
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static List<String> readToStrs(InputStream ins, String charset){
		if (ins == null) {
			return null;
		}
		try {
			return IOUtils.readLines(ins,charset);
		} catch (IOException e) {
			throw new IoRuntimeException("读取InputStream为List<String>失败", e);
		} finally {
			CloseUtil.close(ins);
		}
	}

	/**
	 * 读取Reader为List[String]
	 * 
	 * @param reader	输入流（已关闭）	
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年6月4日
	 */
	public static List<String> readToStrs(Reader reader){
		if (reader == null) {
			return null;
		}
		try {
			return IOUtils.readLines(reader);
		} catch (IOException e) {
			throw new IoRuntimeException("读取Reader为List<String>失败", e);
		} finally {
			CloseUtil.close(reader);
		}
	}
	
	/**
	 * 从InputStream的当前读取位置，跳过指定个字节数。再次read时从跳至处开始读取
	 *
	 * @param ins
	 * @param skip 跳过的字节数，小于1时忽略
	 * @param skipEOF 是否跳至行尾符(true如果遇到行尾符则不再跳过，false直至跳到流末尾)
	 * @return 实际跳过的字节数(如果跳到流末尾，则跳过数会小于指定的skip)；如果指定skipEOF为true，则返回-1
	 */
	public static long skip(InputStream ins, long skip, boolean skipEOF){
		if (ins == null || skip < 1) {
			return 0;
		}
		try {
			// 跳过：不会跳过到流末尾，而是跳过到行尾(遇见行尾符EOF时停止跳过)
			if (skipEOF) {
				IOUtils.skipFully(ins, skip);
				//如果指定跳至EOF则返回-1，而不返回实际跳过的字符数
				return -1;
			}
			return IOUtils.skip(ins, skip);
		} catch (IOException e) {
			throw new IoRuntimeException("跳过InputStream失败", e);
		}
	}
	/**
	 * 从Reader当前读取位置，跳过指定个字符数。再次read时从跳至处开始读取
	 * 
	 * @param reader
	 * @param skip 跳过的字符数，小于1时忽略
	 * @param skipEOF 是否跳至行尾符(true如果遇到行尾符则不再跳过，false直至跳到流末尾)
	 * @return 实际跳过的字符数(如果跳到流末尾，则跳过数会小于指定的skip)；如果指定skipEOF为true，则返回-1
	 */
	public static long skip(Reader reader, long skip, boolean skipEOF){
		if (reader == null || skip < 1) {
			return 0;
		}
		try {
			// 跳过：不会跳过到流末尾，而是跳过到行尾(遇见行尾符EOF时停止跳过)
			if (skipEOF) {
				IOUtils.skipFully(reader, skip);
				//如果指定跳至EOF则返回-1，而不返回实际跳过的字符数
				return -1;
			}
			return IOUtils.skip(reader, skip);
		} catch (IOException e) {
			throw new IoRuntimeException("跳过Reader失败", e);
		}
	}

	
	
}
